/*
 * Copyright 2003 Program of Computer Graphics, Cornell University
 *     580 Rhodes Hall
 *     Cornell University
 *     Ithaca NY 14853
 * Web: http://www.graphics.cornell.edu/
 * 
 * Not for commercial use. Do not redistribute without permission.
 */

#ifndef _H_PARALLELOGRAM
#define _H_PARALLELOGRAM

#include "Geometry.h"
#include <cstdio>

//parallelograms are very similar to triangles but with a different boundary
//condition in barycentric coordinates
class Parallelogram : public GeometryObject {

private:

// The layout for this class if different in 32 and 64 bit, it the later
// the structure will have bo be 80 bytes long, otherwise it wouldn't be
// possible to load all vertices using a single load operation, which
// requires the 16-byte alignment
#ifdef BRITE_64
  BRITE_UNUSED float dummy64a,dummy64b,dummy64c;
#endif

  float v0x,v0y,v0z;
  float v1x,v1y,v1z;
  int mungedId;
  float v2x,v2y,v2z;
  float dummy; //dummy value for when loading v2 as a F32vec4
  float v3x,v3y,v3z;
  float dummy2; //dummy value for when loading v3 as a F32vec4

public:
  Parallelogram() { }
  ~Parallelogram() {
    BRITE_DEBUGBREAK;  //geometry objects are now pool allocated and so their destructor should never be called
  }
  void set(float _v0x, float _v0y, float _v0z, float _v1x, float _v1y, float _v1z,
    float _v2x, float _v2y, float _v2z, float _v3x, float _v3y, float _v3z, int _id) {
    if (_id&~GID_VALUE_MASK) { printf("bad geometry(p) id %d\n",_id);
      BRITE_DEBUGBREAK;
    }
    v0x = _v0x; v0y = _v0y; v0z = _v0z;
    v1x = _v1x; v1y = _v1y; v1z = _v1z;
    v2x = _v2x; v2y = _v2y; v2z = _v2z;
    v3x = _v3x; v3y = _v3y; v3z = _v3z;
    mungedId = _id | GID_FLAG_FP; dummy = dummy2 = 0;
  }
  static Parallelogram *poolAlloc(PoolAllocator *pool) {
    void *ptr = pool->allocate(sizeof(Parallelogram),SIMD_SIZE);
    return new(ptr) Parallelogram();
  }
  virtual int getType() const { return GEOM_PARALLELOGRAM; }
  virtual PrimitiveList *compile(int num,const GeometryObject **array,PoolAllocator *pool)const;
  virtual bool intersectsBox(const AxisAlignedBox &box)const;
  virtual void expandBoxToInclude(AxisAlignedBox *box)const;
  //estimate the bounding box of the portion of the object inside the specified bounding box
  virtual AxisAlignedBox estimateIntersectionBounds(const AxisAlignedBox &box)const;
  //return approximate cost relative to a triangle
  virtual int estimateRelativeCost() const { return 1; }

  int id() const { return mungedId&GID_VALUE_MASK; }
  //get vertices as SIMD vectors of 4 floats (value of fourth float is not specified)
  F32vec4 getV0() const {
#ifndef BRITE_64
    F32vec4 temp = Load_F32vec4((float *)this);
#else
    F32vec4 temp = Load_F32vec4(&this->dummy64c);
#endif
    return _mm_shuffle_ps(temp,temp,_MM_SHUFFLE(3,3,2,1));
  }
  F32vec4 getV1() const { return Load_F32vec4(&this->v1x); }
  F32vec4 getV2() const { return Load_F32vec4(&this->v2x); }
  F32vec4 getV3() const { return Load_F32vec4(&this->v3x); }
  AxisAlignedBox getBoundingBox() const;
  bool intersectsRay(const Ray &ray, IntersectionRecord *hitRec) const;
};


//should we precompute crossproduct of edges for each parallelogram?
//#define PRECOMPUTE_PARALLELOGRAM_CROSSPRODUCT

//types used when we compile triangles into PrimitiveLists for fast intersection testing
class ALIGN16 PackedParallelogram {
public:
#ifdef PRECOMPUTE_PARALLELOGRAM_CROSSPRODUCT
  F32vec4 C;
#endif
  F32vec4 e2;
  F32vec4 e1;
  F32vec4 v0;
  int id;
};

//intersection routine for packed parallelograms
void IntersectPackedParallelograms(class PrimitiveList *, class Ray *ray, class IntersectionRecord *);

class PackedParallelogramList : public PrimitiveList {
public:
  PackedParallelogram paras[1]; //actual number will be specified in length field

  static PackedParallelogramList* poolAlloc(int numRects, PoolAllocator *pool) {
    int size = sizeof(PrimitiveList) + (numRects)*sizeof(PackedParallelogram);
    PackedParallelogramList *list = (PackedParallelogramList*)pool->allocate(size);
    memset(list,0,size);
    list->length = numRects;
    list->setType(GEOM_PACKED_PARALLELOGRAM);
    list->func = IntersectPackedParallelograms;
    return list;
  }
  void setParallelogram(int index, float v0x, float v0y, float v0z, float v1x, float v1y, float v1z,
    float v2x, float v2y, float v2z, float v3x, float v3y, float v3z, int id);
};



#endif //_H_PARALLELOGRAM
